#include <iostream>
using namespace std;
#include <string>
// 多态使用时，如果子类中有属性开辟到堆区，那么父类指针在释放时无法调用到子类的析构代码
// 解决方式：将父类中的析构函数改为虚析构或者纯虚析构

// 虚析构和纯虚析构共性：
// 1.可以解决父类指针释放子类对象
// 2.都需要具有具体的函数实现
// 虚析构和纯虚析构区别
// 1.如果是纯虚析构，该类属于抽象类，不能实例化对象

// 虚析构语法：
//  virtual ~类名() { }
//  纯虚析构语法：
//  virtual ~类名() = 0;
//  纯虚析构函数的实现：
//  ~类名() { } // 纯虚析构函数的实现
//  纯虚析构函数的实现和虚析构函数的实现是一样的

class Animal {
   public:
    Animal() { cout << "Animal构造函数" << endl; }
    // 虚析构函数
    // 利用虚析构函数解决父类指针释放子类对象时不干净的问题
    // 虚析构函数的作用是让父类指针释放子类对象时，调用子类的析构函数
    // virtual ~Animal()
    // {
    //     cout << "Animal虚析构函数" << endl;
    // }
    // 纯虚析构 需要声明也需要实现
    // 有了纯虚析构之后，这个类也属于抽象类，不能实例化对象
    virtual ~Animal() = 0;  // 纯虚析构函数
    // 纯虚函数
    virtual void Speak() = 0;
};

Animal::~Animal() {
    cout << "Animal纯虚析构函数" << endl;
};

class Cat : public Animal {
   public:
    Cat(string name) { m_Name = new string(name); }
    void Speak() { cout << *m_Name << "Cat Speak" << endl; };
    ~Cat() {
        if (m_Name != NULL) {
            cout << "Cat析构函数" << endl;
            // 释放堆区内存
            delete m_Name;  // 释放堆区内存
            m_Name = NULL;  // 将指针置空
        }
    }
    string* m_Name;
};

void test1() {
    // Animal a; // 不能实例化对象
    // Animal *pa = new Animal(); // 不能实例化对象
    Animal* animal = new Cat("Tom");  // 父类指针指向子类对象
    // 父类指针在析构时候，不会调用子类的析构函数，导致子类如果有堆区属性，出现内存泄漏
    animal->Speak();  // Cat Speak
    delete animal;    // 释放父类指针
}

int main() {
    test1();
    system("pause");
    return 0;
}